package com.cloudfun.campusshare.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.cloudfun.campusshare.common.constant.Code;
import com.cloudfun.campusshare.common.constant.DeviceType;
import com.cloudfun.campusshare.common.constant.ProductType;
import com.cloudfun.campusshare.common.exception.BusinessException;
import com.cloudfun.campusshare.common.model.dto.*;
import com.cloudfun.campusshare.common.model.vo.DeviceModeSetDetailVO;
import com.cloudfun.campusshare.common.model.vo.DeviceModeSetListVO;
import com.cloudfun.campusshare.common.model.vo.DeviceModeSetSimpleVO;
import com.cloudfun.campusshare.common.response.PageResult;
import com.cloudfun.campusshare.common.security.AuthenticationHolder;
import com.cloudfun.campusshare.entity.DeviceModeEntity;
import com.cloudfun.campusshare.entity.DeviceModeSetEntity;
import com.cloudfun.campusshare.entity.UserEntity;
import com.cloudfun.campusshare.repo.DeviceModeSetRepo;
import com.cloudfun.campusshare.repo.UserEntityRepo;
import com.cloudfun.campusshare.service.*;
import com.cloudfun.campusshare.util.AmountUtil;
import com.cloudfun.campusshare.util.BeanCopyUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by Huqin on 2020/5/11
 */
@Slf4j
@Service
public class DeviceModeSetServiceImpl extends CreatorPersistenceService implements DeviceModeSetService {

    @Autowired
    private DeviceModeSetRepo deviceModeSetRepo;

    @Autowired
    private DeviceModeService deviceModeService;

    @Autowired
    private UserEntityRepo userEntityRepo;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private UserRelService userRelService;

    @Override
    protected Class<?> getEntityClass() {
        return DeviceModeSetEntity.class;
    }

    @Override
    @Transactional
    public void create(DeviceModeSetCreateDTO createDTO) {
        DeviceModeSetEntity modeSetEntity = new DeviceModeSetEntity();
        BeanUtil.copyProperties(createDTO, modeSetEntity);
        DeviceType type = DeviceType.getDeviceTypeByName(createDTO.getDeviceType());
        if (null == type) {
            throw new BusinessException(Code.PARAMETER_ERROR, "设备类型错误");
        }
        modeSetEntity.setDeviceTypeName(type.getDesc());
        UserEntity userMerchant = userRelService.getUserMerchant(AuthenticationHolder.getUserId());
        Optional.ofNullable(userMerchant).ifPresent(merchant -> {
            modeSetEntity.setMerchantId(merchant.getId());
        });
        deviceModeSetRepo.save(modeSetEntity);
        String modeSetId = modeSetEntity.getId();
        // 保存模式
        List<DeviceModeCreateDTO> modes = createDTO.getModes();
        if (!CollectionUtils.isEmpty(modes)) {
            List<DeviceModeEntity> deviceModeEntities = modes.stream()
                    .map(mode -> {
                        DeviceModeEntity modeEntity = new DeviceModeEntity();
                        modeEntity.setMetaModeId(mode.getMetaModeId());
                        modeEntity.setPrice(AmountUtil.strAmount2Int(mode.getPrice()));
                        if (mode.getDosage()!=null){
                            modeEntity.setDosage(mode.getDosage());
                        }
                        Optional.ofNullable(userMerchant).ifPresent(merchant -> {
                            modeEntity.setMerchantId(merchant.getId());
                        });
                        modeEntity.setModeSetId(modeSetId);
                        return modeEntity;
                    }).collect(Collectors.toList());
            deviceModeService.save(modeSetId, deviceModeEntities);
        }
    }

    @Override
    @Transactional
    public void modify(DeviceModeSetModifyDTO modifyDTO) {
        DeviceModeSetEntity modeSetEntity = deviceModeSetRepo.findById(modifyDTO.getId()).orElseThrow(() -> new BusinessException(Code.NODATA, "模式集不存在"));
        BeanUtil.copyProperties(modifyDTO, modeSetEntity, CopyOptions.create().setIgnoreNullValue(true));
        if (StringUtils.isNotEmpty(modifyDTO.getDeviceType())) {
            DeviceType type = DeviceType.getDeviceTypeByName(modifyDTO.getDeviceType());
            if (null == type) {
                throw new BusinessException(Code.PARAMETER_ERROR, "设备类型错误");
            }
            modeSetEntity.setDeviceTypeName(type.getDesc());
        }
        deviceModeSetRepo.save(modeSetEntity);
        String modeSetId = modeSetEntity.getId();
        // 保存模式
        UserEntity userMerchant = userRelService.getUserMerchant(AuthenticationHolder.getUserId());
        List<DeviceModeModifyDTO> modes = modifyDTO.getModes();
        if (!CollectionUtils.isEmpty(modes)) {
            List<DeviceModeEntity> deviceModeEntities = modes.stream()
                    .map(mode -> {
                        DeviceModeEntity modeEntity = new DeviceModeEntity();
                        modeEntity.setId(mode.getId());
                        modeEntity.setMetaModeId(mode.getMetaModeId());
                        modeEntity.setPrice(AmountUtil.strAmount2Int(mode.getPrice()));
                        if (mode.getDosage()!=null){
                            modeEntity.setDosage(mode.getDosage());
                        }
                        modeEntity.setMerchantId(userMerchant.getId());
                        modeEntity.setModeSetId(modeSetId);
                        return modeEntity;
                    }).collect(Collectors.toList());
            deviceModeService.save(modeSetId, deviceModeEntities);
        }
    }

    @Override
    public PageResult<DeviceModeSetListVO> search(Pageable pageable, DeviceModeSetSearchDTO searchDTO) {
        Specification<DeviceModeSetEntity> spec = this.getSpec(searchDTO);
        Page<DeviceModeSetEntity> result = deviceModeSetRepo.findAll(spec, pageable);
        List<DeviceModeSetListVO> modeSetListVOList = result.getContent().stream()
                .map(this::toListVO).collect(Collectors.toList());
        return new PageResult<>(result.getTotalElements(), modeSetListVOList);
    }

    private Specification<DeviceModeSetEntity> getSpec(DeviceModeSetSearchDTO searchDTO) {
        return (root, query, criteriaBuilder) -> {
            Boolean viewAllData = AuthenticationHolder.getUserAuth().getViewAllData();
            List<Predicate> list = new ArrayList<>();
            if (AuthenticationHolder.isAdmin()) {
                if (!StringUtils.isBlank(searchDTO.getMerchantId())) {
                    list.add(criteriaBuilder.equal(root.get("merchantId"), searchDTO.getMerchantId()));
                }
            } else {
                if (viewAllData) {
                    String userId = AuthenticationHolder.getUserId();
                    UserEntity merchant = userRelService.getUserMerchant(userId);
                    list.add(criteriaBuilder.equal(root.get("merchantId"), merchant.getId()));
                } else {
                    Set<String> allSub = userRelService.getDescendantIds(AuthenticationHolder.getUserId());
                    allSub.add(AuthenticationHolder.getUserId());
                    list.add(root.get("creatorId").in(allSub));
                }
            }
            if (!CollectionUtils.isEmpty(searchDTO.getBrandIds())) {
                list.add(root.get("brandId").in(searchDTO.getBrandIds()));
            }
            if (null != searchDTO.getProductType()) {
                CriteriaBuilder.In<Object> in = criteriaBuilder.in(root.get("productType"));
                String[] productTypes = searchDTO.getProductType().split(",");
                for (int i = 0; i < productTypes.length; i++) {
                    in.value(ProductType.valueOf(productTypes[i]));
                }
                list.add(in);
            }
            if (StringUtils.isNotBlank(searchDTO.getDeviceType())) {
                list.add(criteriaBuilder.equal(root.get("deviceType"), searchDTO.getDeviceType()));
            }
            if (StringUtils.isNotBlank(searchDTO.getKeyword())) {
                List<Predicate> likes = Lists.newArrayList();
                likes.add(criteriaBuilder.like(root.get("name"), "%" + searchDTO.getKeyword() + "%"));
                likes.add(criteriaBuilder.like(root.get("deviceType"), "%" + searchDTO.getKeyword() + "%"));
                likes.add(criteriaBuilder.like(root.get("description"), "%" + searchDTO.getKeyword() + "%"));
                Predicate[] likeArr = new Predicate[likes.size()];
                list.add(criteriaBuilder.or(likes.toArray(likeArr)));
            }
            Predicate[] predicates = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(predicates));
        };
    }

    private DeviceModeSetListVO toListVO(DeviceModeSetEntity deviceModeSetEntity) {
        DeviceModeSetListVO res = BeanCopyUtil.copy(deviceModeSetEntity, DeviceModeSetListVO.class);
        if (null != deviceModeSetEntity.getBrand()) {
            res.setBrandId(deviceModeSetEntity.getBrand().getId());
            res.setBrandName(deviceModeSetEntity.getBrand().getName());
        }
        return res;
    }

    @Override
    public DeviceModeSetDetailVO detail(String id) {
        DeviceModeSetEntity modeSetEntity = deviceModeSetRepo.findById(id).orElseThrow(() -> new BusinessException(Code.NODATA));
        DeviceModeSetDetailVO detailVO = new DeviceModeSetDetailVO();
        BeanUtil.copyProperties(modeSetEntity, detailVO);
        if (null != modeSetEntity.getBrand()) {
            detailVO.setBrandId(modeSetEntity.getBrand().getId());
            detailVO.setBrandName(modeSetEntity.getBrand().getName());
        }
        List<DeviceModeSimpleDTO> modes = deviceModeService.getModesByModeSetId(id);
        detailVO.setModes(modes);

        return detailVO;
    }

    @Override
    @Transactional
    public void delete(String id) {
        // 删除下面关联的模式
        deviceModeService.deleteByModeSetId(id);
        // 设备模式设置为空
        deviceService.updateDeviceModeSetId(id, null);
        deviceModeSetRepo.deleteById(id);
    }

    @Override
    public List<DeviceModeSetSimpleVO> simpleList(DeviceModeSetSearchDTO searchDTO) {
        Specification<DeviceModeSetEntity> spec = getSpec(searchDTO);
        List<DeviceModeSetEntity> entities = deviceModeSetRepo.findAll(spec);
        Set<String> modeSetIds = new HashSet<>();
        List<DeviceModeSetSimpleVO> res = entities.stream().peek(entity -> modeSetIds.add(entity.getId()))
                .map(entity -> BeanCopyUtil.copy(entity, DeviceModeSetSimpleVO.class)).collect(Collectors.toList());
        Map<String, List<DeviceModeSimpleDTO>> modeSetIdSimpleDTOListMap = deviceModeService.getModesByModeSetIds(modeSetIds).stream()
                .collect(Collectors.groupingBy(DeviceModeSimpleDTO::getModeSetId));
        for (DeviceModeSetSimpleVO vo : res) {
            vo.setModes(modeSetIdSimpleDTOListMap.get(vo.getId()));
        }
        return res;
    }
}
